﻿using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Diagnostics;
using Microsoft.Crm.SdkTypeProxy;
using System.Web.Services.Protocols;
using Microsoft.Crm.SdkTypeProxy.Metadata;
using Microsoft.Crm.Sdk.Metadata;
using System.Globalization;

namespace Boomerang.Common.Crm
{
    public class CustomizationsManager
    {
        CrmConnection connection;

        public CustomizationsManager(CrmConnection connection)
        {
            this.connection = connection;
        }

        public void ProvisionCustomizations(string filePath)
        {
            StreamReader customizations;

            try
            {
                customizations = new StreamReader(filePath);
            }
            catch (Exception ex)
            {
                Trace.TraceError("Unable to open customizations XML File from {0} : {1}", filePath, ex.Message);
                throw new IOException(String.Format(CultureInfo.InvariantCulture, "Unable to open customizations XML File from {0} ", filePath), ex);
            }


            try
            {
                ImportAllXmlRequest request = new ImportAllXmlRequest();
                request.CustomizationXml = customizations.ReadToEnd();

                connection.CrmService.Execute(request);
            }
            catch (SoapException soapEx)
            {
                Trace.TraceError("Error importing Customizations: {0}", soapEx.Detail.InnerText);
                throw new IOException(String.Format(CultureInfo.InvariantCulture, "Error importing Customizations: {0} ", soapEx.Detail.InnerText), soapEx);
            }
            catch (Exception ex)
            {
                Trace.TraceError("Error importing Customizations: {0}", ex.Message);
                throw new InvalidOperationException(String.Format(CultureInfo.InvariantCulture, "Error importing Customizations: {0} ", ex.Message), ex);
            }
            finally
            {
                if (customizations != null) customizations.Close();
            }



            try
            {
                PublishXmlRequest publishRequest = new PublishXmlRequest();
                publishRequest.ParameterXml = @"<importexportxml> <entities> <entity>" + CrmSms.SchemaName + @"</entity> </entities> <nodes/> <securityroles/> <settings/> <workflows/> </importexportxml>";

                connection.CrmService.Execute(publishRequest);
            }
            catch (SoapException soapEx)
            {
                Trace.TraceError("Error publishing Customizations: {0}", soapEx.Detail.InnerText);
                throw new InvalidOperationException(String.Format(CultureInfo.InvariantCulture, "Error publishing Customizations: {0} ", soapEx.Detail.InnerText), soapEx);
            }
            catch (Exception ex)
            {
                Trace.TraceError("Error publishing Customizations: {0}", ex.Message);
                throw new InvalidOperationException(String.Format(CultureInfo.InvariantCulture, "Error publishing Customizations: {0} ", ex.Message), ex);
            }

        }

        public void ProvisionWorkflows(string filePath)
        {
            StreamReader customizations;

            try
            {
                customizations = new StreamReader(filePath);
            }
            catch (Exception ex)
            {
                Trace.TraceError("Unable to open customizations XML File from {0} : {1}", filePath, ex.Message);
                throw new IOException(String.Format(CultureInfo.InvariantCulture, "Unable to open customizations XML File from {0} ", filePath), ex);
            }


            try
            {
                ImportAllXmlRequest request = new ImportAllXmlRequest();
                request.CustomizationXml = customizations.ReadToEnd();

                connection.CrmService.Execute(request);
            }
            catch (SoapException soapEx)
            {
                Trace.TraceError("Error importing Workflows: {0}", soapEx.Detail.InnerText);
                throw new InvalidOperationException(String.Format(CultureInfo.InvariantCulture, "Error importing Workflows: {0} ", soapEx.Detail.InnerText), soapEx);
            }
            catch (Exception ex)
            {
                Trace.TraceError("Error importing Workflows: {0}", ex.Message);
                throw new InvalidOperationException(String.Format(CultureInfo.InvariantCulture, "Error importing Workflows: {0} ", ex.Message), ex);
            }
            finally
            {
                if (customizations != null) customizations.Close();
            }

        }

        public void ValidateCustomizations()
        {

            RetrieveEntityRequest request = new RetrieveEntityRequest();
            request.EntityItems = Microsoft.Crm.Sdk.Metadata.EntityItems.All;
            request.LogicalName = CrmSms.SchemaName;
            request.RetrieveAsIfPublished = true;

            RetrieveEntityResponse response = (RetrieveEntityResponse)connection.MetadataService.Execute(request);

            Dictionary<string, AttributeMetadata> attributes = buildAttributeDictionary(response.EntityMetadata.Attributes);

            foreach (string key in CrmSms.Columns)
            {
                if (!attributes.ContainsKey(key))
                    throw new InvalidDataException(string.Format(CultureInfo.InvariantCulture, "Not Valid Customizations. The entity {0} has no attribute {1}", response.EntityMetadata.DisplayName.UserLocLabel.Label, key));
            }

            //TODO: Improve validation
        }

        private static Dictionary<string, AttributeMetadata> buildAttributeDictionary(AttributeMetadata[] attributes)
        {
            Dictionary<string, AttributeMetadata> result = new Dictionary<string, AttributeMetadata>(attributes.Length);

            foreach (AttributeMetadata item in attributes)
            {
                result.Add(item.LogicalName, item);
            }

            return result;
        }
    }
}
